package cn.com.bluemoon.daps.sync.controller;

import cn.com.bluemoon.common.KeyAnnotationClz;
import cn.com.bluemoon.common.UserPermission;
import cn.com.bluemoon.common.permission.ModelEdit;
import cn.com.bluemoon.daps.common.datascope.BmPermission;
import cn.com.bluemoon.daps.common.domain.ResultBean;
import cn.com.bluemoon.daps.common.exception.DapException;
import cn.com.bluemoon.daps.common.mp.IBmService;
import cn.com.bluemoon.daps.model.entity.DapDataModelSubordinateKey;
import cn.com.bluemoon.daps.model.entity.DapDataModelTable;
import cn.com.bluemoon.daps.model.entity.DapDataModelTableField;
import cn.com.bluemoon.daps.sync.base.CompareResult;
import cn.com.bluemoon.daps.sync.base.DevModelCompareResult;
import cn.com.bluemoon.daps.sync.base.ModifyType;
import cn.com.bluemoon.daps.sync.feignclient.DapModelModuleFacade;
import cn.com.bluemoon.daps.sync.service.DapDev2ModelSyncService;
import cn.com.bluemoon.daps.sync.vo.dev2model.*;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.google.common.annotations.VisibleForTesting;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import reactor.util.function.Tuple2;
import springfox.documentation.annotations.ApiIgnore;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author Jarod.Kong
 */
@Api(tags = "导入开发库表")
@RestController
@Slf4j
@Validated
@RequestMapping("/dev2model")
public class DapDev2ModelSyncController {
    @Resource
    private DapDev2ModelSyncService importDevTableService;

    @Resource
    private DapModelModuleFacade modelModuleFacade;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private Environment environment;

    @BmPermission(value = {"model:table:authorize"}, name = "test-delete")
    @DeleteMapping("/testDeleteMapping")
    @ApiOperation(value = "test-delete")
    @ApiResponses(value = {@ApiResponse(code = 500, message = "错误原因信息", response = Boolean.class), @ApiResponse(code = 200, message = "ok", response = DatasourceOut.class)})
    @VisibleForTesting
    @ApiIgnore
    public ResultBean<Boolean> testDel(@RequestParam("modelId") @ApiParam(value = "模型id") String modelId) {
        String[] profiles = environment.getActiveProfiles();
        log.info("request deleteMapping 正常！");
        return ResultBean.ok(true);
    }

    @BmPermission(value = {"model:table:authorize"}, name = "导入开发库-获取模型数据源信息")
    @GetMapping("/database")
    @ApiOperation(value = "导入开发库-确认导入库")
    @ApiResponses(value = {@ApiResponse(code = 500, message = "错误原因信息", response = Boolean.class), @ApiResponse(code = 200, message = "ok", response = DatasourceOut.class)})
    @UserPermission.SingleRow(key = "modelId", keyFormClz = KeyAnnotationClz.RequestParam)
    @ModelEdit(key = "modelId", keyFormClz = KeyAnnotationClz.RequestParam, errorMsg = "当前模型处于发布流程中，不允许导入开发库")
    public ResultBean<DatasourceOut> queryDsInfo(@RequestParam("modelId") @ApiParam(value = "模型id") String modelId) {
        DatasourceOut dsOut = importDevTableService.queryDsInfoByModelId(modelId);
        return ResultBean.ok(dsOut);
    }

    @BmPermission(value = {"model:table:authorize"}, name = "进行库表列表")
    @GetMapping("/table")
    @ApiOperation(value = "导入开发库-确认导入库-下一步")
    @ApiResponses(value = {@ApiResponse(code = 500, message = "错误原因信息", response = Boolean.class), @ApiResponse(code = 200, message = "ok", responseContainer = "List", response = MetaTableOut.class)})
    @UserPermission.SingleRow(key = "modelId", keyFormClz = KeyAnnotationClz.RequestParam)
    @ModelEdit(keyFormClz = KeyAnnotationClz.RequestParam, errorMsg = "当前模型处于发布流程中，不允许导入开发库")
    public ResultBean<List<MetaTableOut>> queryDsTableInfo(@RequestParam("modelId") @ApiParam(value = "模型id") String modelId) {
        List<MetaTableOut> metaTableOuts = importDevTableService.queryDsTableInfoByModelId(modelId);
        return ResultBean.ok(metaTableOuts);
    }

    @BmPermission(value = {"model:table:authorize"}, name = "进行库表与模型表对比")
    @PostMapping("/table/diff")
    @ApiOperation(value = "导入开发库-选择表-下一步、导入开发库-确认表-刷新")
    @ApiResponses(value = {@ApiResponse(code = 500, message = "错误原因信息", response = Boolean.class), @ApiResponse(code = 200, message = "ok", responseContainer = "List", response = CompareResultVo.class)})
    @UserPermission.SingleRow(key = "modelId", keyFormClz = KeyAnnotationClz.RequestBody)
    @ModelEdit(keyFormClz = KeyAnnotationClz.RequestBody, errorMsg = "当前模型处于发布流程中，不允许导入开发库")
    public ResultBean<IPage<CompareResultVo>> compareTables(@Validated @RequestBody Sync2ModelPageVo pageVo) {
        Tuple2<Map<ModifyType, Integer>, List<CompareResult<DevModelCompareResult>>> tuple2 = importDevTableService.compareDevAndModelTablesAndAnalyse(pageVo.getModelId(), pageVo.getTableNames());
        Map<ModifyType, Integer> reportMap = tuple2.getT1();
        List<CompareResult<DevModelCompareResult>> res = tuple2.getT2();
        IPage<CompareResult<DevModelCompareResult>> page = IBmService.logicPage(res, pageVo.getCurrent(), pageVo.getPageSize());
        int tableA = reportMap.getOrDefault(ModifyType.ALTER, 0);
        int tableD = reportMap.getOrDefault(ModifyType.DROP, 0);
        int tableC = reportMap.getOrDefault(ModifyType.CREATE, 0);
        String report = String.format("导入后将有%d个表被修改，%d个表被删除，%d个表被新增", tableA, tableD, tableC);
        return ResultBean.page(page).convert(toVoFunction()).addMoreData("compareReport", report);
    }

    /**
     * out convert
     *
     * @return res
     */
    private Function<IPage<CompareResult<DevModelCompareResult>>, IPage<CompareResultVo>> toVoFunction() {
        return compareResultIPage -> compareResultIPage.convert(c -> {
            DevModelCompareResult src = c.getData();
            CompareResultVo tar = new CompareResultVo();
            BeanUtils.copyProperties(src, tar);
            tar.setTableName(c.getTableName());
            Map<String, List<Object>> desc = c.getDesc();
            List<String> modifyContent = desc.entrySet().stream().map(d -> {
                List<Object> v2AndV1Desc = d.getValue();
                String fieldName = DapDataModelTableField.includeFieldAndDescMap.getOrDefault(d.getKey(), null);
                if (fieldName == null) {
                    fieldName = DapDataModelSubordinateKey.includeFieldAndDescMap.getOrDefault(d.getKey(), null);
                    if (fieldName == null) {
                        fieldName = DapDataModelTable.includeFieldAndDescMap.getOrDefault(d.getKey(), d.getKey());
                    }
                }
                return String.format("%s，库表：%s，模型：%s", fieldName, v2AndV1Desc.get(1), v2AndV1Desc.get(0));
            }).collect(Collectors.toList());
            tar.setModifyContent(modifyContent);
            return tar;
        });
    }

    @BmPermission(value = {"model:table:authorize"}, name = "进行库表与模型表对比且入库")
    @PostMapping("/table/diff/import")
    @ApiOperation(value = "导入开发库-确认表-导入")
    @ApiResponses(value = {@ApiResponse(code = 500, message = "错误原因信息", response = Boolean.class), @ApiResponse(code = 200, message = "ok", response = String.class)})
    @UserPermission.SingleRow(key = "modelId", keyFormClz = KeyAnnotationClz.RequestBody)
    @ModelEdit(keyFormClz = KeyAnnotationClz.RequestBody, errorMsg = "当前模型处于发布流程中，不允许导入开发库")
    public ResultBean<String> compareResult2DB(@Validated @RequestBody Sync2ModelVo vo) {
        // 2022/4/17 增加分布式模型（id）锁 ，也可以考虑模型+所选表的锁
        final String redisLockId = "findLock::" + vo.getModelId();
        RLock modelLock = redissonClient.getLock(redisLockId);
        if (modelLock.isLocked()) {
            throw new DapException("模型导入进行中，不可重复导入！");
        }
        List<CompareResult<DevModelCompareResult>> res;
        try {
            modelLock.lock();
            log.info("对模型{}导入开发库加锁", redisLockId);
            res = importDevTableService.compareDevAndModelTables2DB(vo);
        } finally {
            modelLock.unlock();
            log.info("模型{}解锁完成", redisLockId);
        }
        res.forEach(c -> {
            if (c.getTableId() != null) {
                modelModuleFacade.updateTableUpdateTime(c.getModelId(), c.getTableId());
            }
        });
        return res.isEmpty() ? ResultBean.ok("库表与模型无差异，无需同步") : ResultBean.ok("同步成功");
    }

}
